/***************************************************************************
 * Copyright 2012-2013 TXT e-solutions SpA
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the contract ICT-257367.
 *
 * Authors:
 *      Salvatore Piccione (TXT e-solutions SpA)
 *
 * Contributors:
 *        Domenico Rotondi (TXT e-solutions SpA)
 **************************************************************************/
package it.txt.ens.client.publisher.example.osgi;

import it.txt.ens.client.core.ENSEvent;
import it.txt.ens.client.publisher.ENSPublisher;
import it.txt.ens.client.publisher.ENSPublishingException;
import it.txt.ens.client.publisher.factory.ENSEventFactory;

import java.io.IOException;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;

/**
 * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT txtgroup.com)
 * @author Domenico Rotondi (TXT e-solutions SpA - domenico.rotondi AT txtgroup.com)
 *
 */
class ConfiguredPublisherTracker implements Runnable {
    
    private static final long LOOKUP_TIMEOUT = 10000;
    private final Logger TRACKER_LOGGER = Logger.getLogger(ConfiguredPublisherTracker.class.getName());
    private static final ResourceBundle TRACKER_LOGGING_MESSAGES = ResourceBundle.getBundle(
            "logging-messages/" + ConfiguredPublisherTracker.class.getSimpleName(), Locale.ROOT);
    private final PublisherFilter filter;
    private volatile BundleContext context;
    private final long sleepTime;
    private final Random random;
    private ENSEventFactory eventFactory;
    private ServiceReference<ENSEventFactory> eventFactorySR;
    private ServiceReference<ENSPublisher> publisherSR;
    private String id;
    private final int max;
    private final int min;
    private volatile boolean run;
    
    public ConfiguredPublisherTracker (BundleContext context, PublisherFilter filter, ServiceReference<ENSEventFactory> factoryReference, long sleepTime,
            int max, int min) {
        this.filter = filter;
        this.context = context;
        this.sleepTime = sleepTime;
        this.eventFactorySR = factoryReference;
        this.eventFactory = context.getService(eventFactorySR);
        this.max = max;
        this.min = min;
        random = new Random();
        run = true;
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
//        ServiceTracker<ENSPublisher, ENSPublisher> tracker = new ServiceTracker<ENSPublisher, ENSPublisher>(context, filter, null);
//        tracker.open();
        ENSPublisher publisher = null;
        Collection<ServiceReference<ENSPublisher>> publisherReferences;
        long startTime = System.currentTimeMillis();
        try {
            do {
                publisherReferences = context.getServiceReferences(ENSPublisher.class, filter.toString());
                if (!publisherReferences.isEmpty())
                    publisher = selectPublisher(publisherReferences);
            } while (publisher == null && (System.currentTimeMillis() - startTime) <= LOOKUP_TIMEOUT);
//            publisher = tracker.waitForService(LOOKUP_TIMEOUT);
        } catch (InvalidSyntaxException e) {
            TRACKER_LOGGER.log(Level.SEVERE, TRACKER_LOGGING_MESSAGES.getString("badFilter"), e);
        }
//        tracker.close();
        if (publisher == null) {
            TRACKER_LOGGER.log(Level.SEVERE, "No publisher found with selection criteria " + filter.toString());
            return;
        }
        id = publisher.getTargetResource().getNamespace() + "@" + publisher.getTargetResource().getPattern();
        //TODO register printers in the OSGi service registry
        try {
            publisher.connect();
        } catch (Exception e) {
            TRACKER_LOGGER.log(Level.SEVERE, MessageFormat.format( 
                    TRACKER_LOGGING_MESSAGES.getString("errorWhileStartingPublisher"),id),e);
            return;
        }
        
        ENSEvent event;
        Map<String, Object> headers = null;
        byte[] payload;
        Date timestamp;
        double simulatedMeasure;
        int salt;
        int maxRandom = max - min + 1;
        
        while (run) {
            salt = random.nextInt(maxRandom) + min;
            simulatedMeasure = random.nextDouble() * salt;
            payload = String.valueOf(simulatedMeasure).getBytes(Charset.forName(ENSEvent.DEFAULT_CONTENT_ENCODING));
            timestamp = new Date();
            event = eventFactory.create(headers, payload, timestamp, false);
            try {
                publisher.publish(event);
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {}
            } catch (ENSPublishingException e) {
                TRACKER_LOGGER.log(Level.WARNING, MessageFormat.format(
                        TRACKER_LOGGING_MESSAGES.getString("unpublishedEvent"), event.toString()), e);
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e1) {}
                continue;
            }
        }
        
        if (TRACKER_LOGGER.isLoggable(Level.FINE))
            TRACKER_LOGGER.log(Level.FINE, MessageFormat.format(TRACKER_LOGGING_MESSAGES.getString("publisherShuttingDown"),id));
        
        
        publisher.disconnect();
        
        //delete the publisher configuration
        ServiceReference<ConfigurationAdmin> configAdminSR = context.getServiceReference(ConfigurationAdmin.class);
        ConfigurationAdmin configAdmin = context.getService(configAdminSR);
        String pid = (String) publisherSR.getProperty(Constants.SERVICE_PID);
        if (pid != null) {
            try {
                Configuration config = configAdmin.getConfiguration(pid );
                if (config != null)
                    config.delete();
            } catch (IOException e) {
                if (TRACKER_LOGGER.isLoggable(Level.WARNING))
                    TRACKER_LOGGER.log(Level.WARNING, 
                        MessageFormat.format(TRACKER_LOGGING_MESSAGES.getString("configurationDeletionError"), pid), e);
            }
        }
        context.ungetService(eventFactorySR);
        context.ungetService(publisherSR);
        context.ungetService(configAdminSR);
        if (TRACKER_LOGGER.isLoggable(Level.INFO)) {
            TRACKER_LOGGER.log(Level.INFO, MessageFormat.format(TRACKER_LOGGING_MESSAGES.getString("publisherShutDown"), id));
        }
//
//            if (properties != null) {
//                StringBuilder builder = new StringBuilder();
//                boolean isFirst = true;
//                java.util.Enumeration<String> keys = properties.keys();
//                String key;
//                while (keys.hasMoreElements()) {
//                    key = keys.nextElement();
//                    if (isFirst)
//                        isFirst = false;
//                    else
//                        builder.append("\n");
//                    builder.append(key);
//                    builder.append(": ");
//                    builder.append(properties.get(key));
//                }
//                LOGGER.log(Level.FINER, "Registering an instance of " + ENSEventPrinter.class.getName() + 
//                        " with pid " + pid + "and the following properties:\n" + builder.toString());
//            }
    
    }
    
    private ENSPublisher selectPublisher (Collection<ServiceReference<ENSPublisher>> publisherReferences) {
        ServiceReference<ENSPublisher> reference;
        Iterator<ServiceReference<ENSPublisher>> iterator = publisherReferences.iterator();
        ENSPublisher publisher = null;
        while (iterator.hasNext() && publisher == null) {
            reference = iterator.next();
            if (filter.match(reference)) {
                publisherSR = reference;
                publisher = context.getService(reference);
            }
        }
        return publisher;
    }
    
    public void shutdown (BundleContext context) {
        run = false;
        this.context = context;
        if (TRACKER_LOGGER.isLoggable(Level.FINE))
            TRACKER_LOGGER.log(Level.FINE, MessageFormat.format(TRACKER_LOGGING_MESSAGES.getString("shutDownRequest"),id));
    }
}
